﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing;
using System.Windows.Forms;
using CommonLib.Common;
using CommonLib.Engine;
using CommonLib.Geometry;
using System.IO;
namespace RayTracerLib.Engine
{
    /// <summary>
    /// Отвечает за основую работу по визуализации сцены
    /// </summary>
    public class Engine : IDisposable
    {
        public Device Device { get; set; }
        public Control PresentControl { get; set; }        
        public Engine(Control control)
        {
            PresentControl = control;
            var parameters = new PresentParameters {Windowed = true, SwapEffect = SwapEffect.Discard};
            Device = new Device(0, DeviceType.Hardware, control, CreateFlags.SoftwareVertexProcessing, parameters);            
        }
        VertexBuffer _vb;
        public void Render(Scene scene, Camera camera, Settings settings, string outFile)
        {
            _vb = new VertexBuffer(typeof(CustomVertex.TransformedColored), 4, Device, 0, CustomVertex.PositionTextured.Format, Pool.Default);
            Device.Clear(ClearFlags.Target, Color.FromArgb(scene.Background.ToInt()), 1, 0);
            Device.BeginScene();            
            Device.VertexFormat = CustomVertex.TransformedColored.Format;
            Vector3 n = camera.n;
            Vector3 u = camera.u;
            Vector3 v = camera.v;
            for (int row = 0; row < Math.Min(settings.Resolution.Height,PresentControl.Height); row += settings.BlockSize)
                for (int col = 0; col <Math.Min( settings.Resolution.Width,PresentControl.Width); col += settings.BlockSize)
                {
                    float x = -camera.W + (float)col * 2 * camera.W / (float)settings.Resolution.Width;
                    float y = -camera.H + (float)row * 2 * camera.H / (float)settings.Resolution.Height;
                    var ray = new Ray(camera.Eye,
                        new Vector3(
                        -camera.nearDist * n.X + x * u.X + y * v.X,
                        -camera.nearDist * n.Y + x * u.Y + y * v.Y,
                        -camera.nearDist * n.Z + x * u.Z + y * v.Z));
                    //errors.Text += "row = " + row.ToString() + " col=" + col.ToString() + (char)'\r'+(char)'\n';
                    DrawRectangle(row, col, settings.ShadingClass.Shade(ray, scene).ToInt(), settings);
                }
            Device.EndScene();
            Device.Present();
            try
            {
                SurfaceLoader.Save(Path.Combine(Application.StartupPath, outFile), ImageFileFormat.Bmp, Device.GetBackBuffer(0, 0, BackBufferType.Mono));//позволяет сохранить в файл
            }
            catch
            { MessageBox.Show("Не удалось сохранить в файл"); }

        }
        public void Dispose()
        {            
            Device.Dispose();
        }
            
        private void DrawRectangle(int row, int col, int clr, Settings settings)
        {            
            GraphicsStream str = _vb.Lock(0, 0, 0);
            var quad = new CustomVertex.TransformedColored[4];            
            for (int i = 0; i < 4; i++)
                quad[i].Color = clr;
            float width = (float)PresentControl.Width / settings.Resolution.Width * settings.BlockSize;
            float height = (float)PresentControl.Height / settings.Resolution.Height * settings.BlockSize;
            quad[0].Position = new Vector4(width * col, height * row, 0, 0);
            quad[1].Position = new Vector4(width * (col + 1), height * row, 0, 0);
            quad[2].Position = new Vector4(width * (col + 1), height * (row + 1), 0, 0);
            quad[3].Position = new Vector4(width * col, height * (row + 1), 0, 0);
            str.Write(quad);
            _vb.Unlock();
            Device.SetStreamSource(0, _vb, 0);
            Device.VertexFormat = CustomVertex.TransformedColored.Format;
            Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);            
        }
    }
}
